using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ConsoleTables;
using SqlInjectionScanner.Models;

namespace SqlInjectionScanner.Core
{
    /// <summary>
    /// 报告生成器
    /// </summary>
    public class ReportGenerator
    {
        /// <summary>
        /// 生成控制台报告
        /// </summary>
        /// <param name="results">扫描结果</param>
        /// <param name="statistics">扫描统计信息</param>
        public void GenerateConsoleReport(List<ScanResult> results, ScanStatistics statistics)
        {
            Console.WriteLine();
            Console.WriteLine("=== SQL注入扫描报告 ===");
            Console.WriteLine();
            
            // 显示统计信息
            Console.WriteLine($"扫描开始时间: {statistics.StartTime}");
            Console.WriteLine($"扫描结束时间: {statistics.EndTime}");
            Console.WriteLine($"扫描持续时间: {statistics.Duration.TotalSeconds:F2} 秒");
            Console.WriteLine($"扫描数据库数量: {statistics.DatabaseCount}");
            Console.WriteLine($"扫描表数量: {statistics.TableCount}");
            Console.WriteLine($"扫描列数量: {statistics.ColumnCount}");
            Console.WriteLine($"扫描记录数量: {statistics.RecordCount}");
            Console.WriteLine($"发现可疑记录数量: {statistics.SuspiciousRecordCount}");
            Console.WriteLine();
            
            // 显示风险等级统计
            Console.WriteLine("风险等级统计:");
            foreach (var level in statistics.IssuesByRiskLevel)
            {
                Console.WriteLine($"  {level.Key}: {level.Value}");
            }
            Console.WriteLine();
            
            // 显示注入类型统计
            Console.WriteLine("注入类型统计:");
            foreach (var type in statistics.IssuesByType.Where(t => t.Value > 0))
            {
                Console.WriteLine($"  {type.Key}: {type.Value}");
            }
            Console.WriteLine();

            // 如果没有发现问题，直接返回
            if (results.Count == 0)
            {
                Console.WriteLine("未发现任何SQL注入问题。");
                return;
            }

            // 按风险等级分组显示结果
            var groupedResults = results
                .GroupBy(r => r.RiskLevel)
                .OrderByDescending(g => g.Key);

            foreach (var group in groupedResults)
            {
                Console.WriteLine($"=== {group.Key} 风险等级 ({group.Count()} 个问题) ===");
                Console.WriteLine();
                
                var table = new ConsoleTable("数据库", "表名", "列名", "注入类型", "可疑值");
                
                foreach (var result in group.Take(10)) // 限制每个风险等级显示的结果数量
                {
                    // 截断可疑值，避免显示过长
                    string truncatedValue = result.SuspiciousValue.Length > 50 
                        ? result.SuspiciousValue.Substring(0, 47) + "..." 
                        : result.SuspiciousValue;
                    
                    table.AddRow(
                        result.DatabaseName,
                        result.TableName,
                        result.ColumnName,
                        result.InjectionType,
                        truncatedValue
                    );
                }
                
                table.Write();
                Console.WriteLine();
                
                if (group.Count() > 10)
                {
                    Console.WriteLine($"... 还有 {group.Count() - 10} 个问题未显示");
                    Console.WriteLine();
                }
            }
            
            // 显示修复建议
            Console.WriteLine("=== 修复建议 ===");
            Console.WriteLine();
            
            var remediationAdvice = results
                .GroupBy(r => r.InjectionType)
                .ToDictionary(g => g.Key, g => g.First().RemediationAdvice);
            
            foreach (var advice in remediationAdvice)
            {
                Console.WriteLine($"{advice.Key}:");
                Console.WriteLine($"  {advice.Value}");
                Console.WriteLine();
            }
        }

        /// <summary>
        /// 生成CSV报告
        /// </summary>
        /// <param name="results">扫描结果</param>
        /// <param name="statistics">扫描统计信息</param>
        /// <param name="filePath">文件路径</param>
        public void GenerateCsvReport(List<ScanResult> results, ScanStatistics statistics, string filePath)
        {
            using var writer = new StreamWriter(filePath, false, Encoding.UTF8);
            
            // 写入标题行
            writer.WriteLine("数据库,表名,列名,数据类型,主键列,主键值,风险等级,注入类型,可疑值,匹配模式,修复建议");
            
            // 写入数据行
            foreach (var result in results)
            {
                writer.WriteLine(
                    $"\"{EscapeCsvField(result.DatabaseName)}\"," +
                    $"\"{EscapeCsvField(result.TableName)}\"," +
                    $"\"{EscapeCsvField(result.ColumnName)}\"," +
                    $"\"{EscapeCsvField(result.DataType)}\"," +
                    $"\"{EscapeCsvField(result.PrimaryKeyColumn)}\"," +
                    $"\"{EscapeCsvField(result.PrimaryKeyValue)}\"," +
                    $"\"{result.RiskLevel}\"," +
                    $"\"{result.InjectionType}\"," +
                    $"\"{EscapeCsvField(result.SuspiciousValue)}\"," +
                    $"\"{EscapeCsvField(result.MatchedPattern.Name)}\"," +
                    $"\"{EscapeCsvField(result.RemediationAdvice)}\""
                );
            }
            
            // 写入统计信息
            writer.WriteLine();
            writer.WriteLine("统计信息");
            writer.WriteLine($"扫描开始时间,{statistics.StartTime}");
            writer.WriteLine($"扫描结束时间,{statistics.EndTime}");
            writer.WriteLine($"扫描持续时间(秒),{statistics.Duration.TotalSeconds:F2}");
            writer.WriteLine($"扫描数据库数量,{statistics.DatabaseCount}");
            writer.WriteLine($"扫描表数量,{statistics.TableCount}");
            writer.WriteLine($"扫描列数量,{statistics.ColumnCount}");
            writer.WriteLine($"扫描记录数量,{statistics.RecordCount}");
            writer.WriteLine($"发现可疑记录数量,{statistics.SuspiciousRecordCount}");
            
            // 写入风险等级统计
            writer.WriteLine();
            writer.WriteLine("风险等级统计");
            foreach (var level in statistics.IssuesByRiskLevel)
            {
                writer.WriteLine($"{level.Key},{level.Value}");
            }
            
            // 写入注入类型统计
            writer.WriteLine();
            writer.WriteLine("注入类型统计");
            foreach (var type in statistics.IssuesByType.Where(t => t.Value > 0))
            {
                writer.WriteLine($"{type.Key},{type.Value}");
            }
        }

        /// <summary>
        /// 生成HTML报告
        /// </summary>
        /// <param name="results">扫描结果</param>
        /// <param name="statistics">扫描统计信息</param>
        /// <param name="filePath">文件路径</param>
        public void GenerateHtmlReport(List<ScanResult> results, ScanStatistics statistics, string filePath)
        {
            using var writer = new StreamWriter(filePath, false, Encoding.UTF8);
            
            writer.WriteLine("<!DOCTYPE html>");
            writer.WriteLine("<html lang=\"zh-CN\">");
            writer.WriteLine("<head>");
            writer.WriteLine("    <meta charset=\"UTF-8\">");
            writer.WriteLine("    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
            writer.WriteLine("    <title>SQL注入扫描报告</title>");
            writer.WriteLine("    <style>");
            writer.WriteLine("        body { font-family: Arial, sans-serif; margin: 20px; }");
            writer.WriteLine("        h1, h2, h3 { color: #333; }");
            writer.WriteLine("        table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }");
            writer.WriteLine("        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }");
            writer.WriteLine("        th { background-color: #f2f2f2; }");
            writer.WriteLine("        tr:nth-child(even) { background-color: #f9f9f9; }");
            writer.WriteLine("        .critical { background-color: #ffdddd; }");
            writer.WriteLine("        .high { background-color: #ffe6cc; }");
            writer.WriteLine("        .medium { background-color: #ffffcc; }");
            writer.WriteLine("        .low { background-color: #e6ffe6; }");
            writer.WriteLine("        .summary { display: flex; flex-wrap: wrap; }");
            writer.WriteLine("        .summary-box { border: 1px solid #ddd; padding: 10px; margin: 10px; flex: 1; min-width: 200px; }");
            writer.WriteLine("        .truncated { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }");
            writer.WriteLine("    </style>");
            writer.WriteLine("</head>");
            writer.WriteLine("<body>");
            writer.WriteLine("    <h1>SQL注入扫描报告</h1>");
            
            // 写入统计信息
            writer.WriteLine("    <h2>扫描统计</h2>");
            writer.WriteLine("    <div class=\"summary\">");
            writer.WriteLine("        <div class=\"summary-box\">");
            writer.WriteLine("            <h3>基本信息</h3>");
            writer.WriteLine("            <p>扫描开始时间: " + statistics.StartTime + "</p>");
            writer.WriteLine("            <p>扫描结束时间: " + statistics.EndTime + "</p>");
            writer.WriteLine("            <p>扫描持续时间: " + statistics.Duration.TotalSeconds.ToString("F2") + " 秒</p>");
            writer.WriteLine("        </div>");
            writer.WriteLine("        <div class=\"summary-box\">");
            writer.WriteLine("            <h3>扫描范围</h3>");
            writer.WriteLine("            <p>扫描数据库数量: " + statistics.DatabaseCount + "</p>");
            writer.WriteLine("            <p>扫描表数量: " + statistics.TableCount + "</p>");
            writer.WriteLine("            <p>扫描列数量: " + statistics.ColumnCount + "</p>");
            writer.WriteLine("            <p>扫描记录数量: " + statistics.RecordCount + "</p>");
            writer.WriteLine("        </div>");
            writer.WriteLine("        <div class=\"summary-box\">");
            writer.WriteLine("            <h3>风险等级统计</h3>");
            foreach (var level in statistics.IssuesByRiskLevel)
            {
                writer.WriteLine("            <p>" + level.Key + ": " + level.Value + "</p>");
            }
            writer.WriteLine("        </div>");
            writer.WriteLine("        <div class=\"summary-box\">");
            writer.WriteLine("            <h3>注入类型统计</h3>");
            foreach (var type in statistics.IssuesByType.Where(t => t.Value > 0))
            {
                writer.WriteLine("            <p>" + type.Key + ": " + type.Value + "</p>");
            }
            writer.WriteLine("        </div>");
            writer.WriteLine("    </div>");
            
            // 如果没有发现问题，显示提示信息
            if (results.Count == 0)
            {
                writer.WriteLine("    <h2>扫描结果</h2>");
                writer.WriteLine("    <p>未发现任何SQL注入问题。</p>");
            }
            else
            {
                // 按风险等级分组显示结果
                var groupedResults = results
                    .GroupBy(r => r.RiskLevel)
                    .OrderByDescending(g => g.Key);
                
                writer.WriteLine("    <h2>扫描结果</h2>");
                
                foreach (var group in groupedResults)
                {
                    string cssClass = group.Key.ToString().ToLower();
                    writer.WriteLine("    <h3>" + group.Key + " 风险等级 (" + group.Count() + " 个问题)</h3>");
                    writer.WriteLine("    <table>");
                    writer.WriteLine("        <tr>");
                    writer.WriteLine("            <th>数据库</th>");
                    writer.WriteLine("            <th>表名</th>");
                    writer.WriteLine("            <th>列名</th>");
                    writer.WriteLine("            <th>主键信息</th>");
                    writer.WriteLine("            <th>注入类型</th>");
                    writer.WriteLine("            <th>匹配模式</th>");
                    writer.WriteLine("            <th>可疑值</th>");
                    writer.WriteLine("        </tr>");
                    
                    foreach (var result in group)
                    {
                        writer.WriteLine("        <tr class=\"" + cssClass + "\">");
                        writer.WriteLine("            <td>" + HtmlEncode(result.DatabaseName) + "</td>");
                        writer.WriteLine("            <td>" + HtmlEncode(result.TableName) + "</td>");
                        writer.WriteLine("            <td>" + HtmlEncode(result.ColumnName) + "</td>");
                        writer.WriteLine("            <td>" + (string.IsNullOrEmpty(result.PrimaryKeyColumn) ? "-" : HtmlEncode(result.PrimaryKeyColumn + ": " + result.PrimaryKeyValue)) + "</td>");
                        writer.WriteLine("            <td>" + result.InjectionType + "</td>");
                        writer.WriteLine("            <td>" + HtmlEncode(result.MatchedPattern.Name) + "</td>");
                        writer.WriteLine("            <td class=\"truncated\" title=\"" + HtmlEncode(result.SuspiciousValue) + "\">" + HtmlEncode(result.SuspiciousValue) + "</td>");
                        writer.WriteLine("        </tr>");
                    }
                    
                    writer.WriteLine("    </table>");
                }
                
                // 写入修复建议
                writer.WriteLine("    <h2>修复建议</h2>");
                
                var remediationAdvice = results
                    .GroupBy(r => r.InjectionType)
                    .ToDictionary(g => g.Key, g => g.First().RemediationAdvice);
                
                writer.WriteLine("    <table>");
                writer.WriteLine("        <tr>");
                writer.WriteLine("            <th>注入类型</th>");
                writer.WriteLine("            <th>修复建议</th>");
                writer.WriteLine("        </tr>");
                
                foreach (var advice in remediationAdvice)
                {
                    writer.WriteLine("        <tr>");
                    writer.WriteLine("            <td>" + advice.Key + "</td>");
                    writer.WriteLine("            <td>" + HtmlEncode(advice.Value) + "</td>");
                    writer.WriteLine("        </tr>");
                }
                
                writer.WriteLine("    </table>");
            }
            
            // 写入页脚
            writer.WriteLine("    <hr>");
            writer.WriteLine("    <p><em>报告生成时间: " + DateTime.Now + "</em></p>");
            writer.WriteLine("</body>");
            writer.WriteLine("</html>");
        }

        /// <summary>
        /// 转义CSV字段
        /// </summary>
        /// <param name="field">字段值</param>
        /// <returns>转义后的字段值</returns>
        private string EscapeCsvField(string? field)
        {
            if (string.IsNullOrEmpty(field))
                return string.Empty;
            
            return field.Replace("\"", "\"\"");
        }

        /// <summary>
        /// HTML编码
        /// </summary>
        /// <param name="text">文本</param>
        /// <returns>编码后的文本</returns>
        private string HtmlEncode(string? text)
        {
            if (string.IsNullOrEmpty(text))
                return string.Empty;
            
            return text
                .Replace("&", "&amp;")
                .Replace("<", "&lt;")
                .Replace(">", "&gt;")
                .Replace("\"", "&quot;")
                .Replace("'", "&#39;");
        }
    }
}